<?php
declare (strict_types = 1);

namespace app\service\logic\common\order;

use app\exception\LogicException;
use app\facade\ALiPayFacade;
use app\facade\StringFacade;
use app\facade\WechatpayFacade;
use app\model\communal\OrderMultiModel;
use app\model\communal\OrderMultiSubModel;
use app\model\communal\ScaleMultiModel;
use app\model\communal\ScaleMultiTopicModel;
use app\model\communal\ScaleMultiTypeModel;
use app\service\logic\BaseService;
use app\service\logic\common\scale\MultiExpertService;
use app\service\logic\common\scale\MultiService;
use app\service\logic\common\user\StatisticsService;
use Carbon\Carbon;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\facade\Db;
use think\facade\Log;

final class OrderMultiService extends BaseService
{
    protected string $modelClassName = 'OrderMultiModel';

    /**
     * 生成订单 debug
     *
     * @param $params
     * @return mixed
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function create($params)
    {
        $multiModel = ScaleMultiModel::findOrFail($params['id']);
        $amount = $multiModel['amount']; // 总金额
        // 获取可用元心币
        $availableCoin = invoke(StatisticsService::class)->getCoinAvailableCountByUserId($params['user_id']);
        $deductCoin = 1 == $params['is_coin'] ? $availableCoin : '0'; // 抵扣用元心币数量

        // 当前所有相关类型
        $typeJson = ScaleMultiTypeModel::field(['id', 'name', 'lowest_desc', 'next_highest_desc', 'highest_desc'])
            ->withTrashed()
            ->where('scale_multi_id', $multiModel['id'])
            ->select()
            ->toJson();
        // 当前所有可用题目
        $topicJson = ScaleMultiTopicModel::field(['id', 'scale_multi_type_id', 'name', "'' as answer", 'options'])
            ->where('scale_multi_id', $multiModel['id'])
            ->select()
            ->toJson();

        list($paymentAmount, $deductAmount, $deductCoin) = StringFacade::orderPayment($amount, $deductCoin);

        $orderInsert = [
            'user_id' => $params['user_id'],
            'scale_multi_id' => $multiModel['id'],
            'scale_multi_name' => $multiModel['name'],
            'scale_multi_image' => $multiModel['image'],
            'scale_multi_intro' => $multiModel['intro'],
            'total_amount' => $amount,
            'payment_amount' => $paymentAmount,
            'deduct_amount' => $deductAmount,
            'deduct_coin' => $deductCoin,
            'payment_type' => $params['payment_type'],
            'type' => $paymentAmount > 0 ? 1 : 2,
        ];
        $orderSubInsert = [
            'order_multi_type' => $typeJson,
            'order_multi_topic' => $topicJson,
        ];

        Db::connect('communal')->transaction(function () use ($orderInsert, $orderSubInsert, $multiModel, &$model) {
            $model = OrderMultiModel::create($orderInsert);
            $number = StringFacade::createStringById($model['id'], 'OM', 16);
            $model->save(['number' => $number]);

            // 附表
            $orderSubInsert['order_multi_id'] = $model['id'];
            OrderMultiSubModel::create($orderSubInsert);

            // 支付成功前需要锁定元心币
            if ($orderInsert['deduct_coin'] > 0) {
                // 需要锁定
                invoke(StatisticsService::class)->freezeCoin($model['user_id'], $model['deduct_coin']);
            }
        });
        // return $model->bindAttr('hasSub'); // 框架无效
        // return OrderMultiModel::with(['hasSub'])->find($model['id']);

        // todo 支付成功后抵扣用的元心币需要扣除
        // todo 支付成功后台 销量 + 1
        // $multiModel->save(['sales_number' => $multiModel['sales_number'] + 1]);
        return $model;
    }

    /**
     * 开始答题
     *
     * @param $params
     * @return mixed
     * @throws DataNotFoundException
     * @throws LogicException
     * @throws ModelNotFoundException
     */
    public function topic($params)
    {
        $model = $this->read($params, ['hasSub']);
        if (2 != $model['type']) {
            throw new LogicException('订单处于非答题状态');
        }

        return $model['order_multi_topic'];
    }

    /**
     * 答题(一题一答)
     *
     * @param $params
     * @return mixed
     * @throws DataNotFoundException
     * @throws LogicException
     * @throws ModelNotFoundException
     */
    public function answer($params)
    {
        $model = $this->read($params, ['hasSub']);

        if (2 != $model['type']) {
            throw new LogicException('订单处于非答题状态');
        }

        $existTopic = false;
        $topicId = $params['topic_id'];
        $answer = $params['answer'];
        $topic = $model['order_multi_topic'];
        foreach ($topic as $key => $item) {
            if ($topicId == $item['id']) {
                $existTopic = true;
                // 验证答案
                $existsAnswer = array_column($item['options'], 'index');
                if (!in_array($answer, $existsAnswer)) {
                    $currentT = $key + 1;
                    throw new LogicException('第 ' . $currentT . ' 题,' . '测试答案超出范围');
                }
                $topic[$key]['answer'] = $answer;
            }
        }

        if (!$existTopic) {
            throw new LogicException('测试题目不存在');
        }

        return $model->hasSub->save(['order_multi_topic' => json_encode($topic, JSON_UNESCAPED_UNICODE)]);
    }

    /**
     * 答题(全部完成)
     *
     * @param $params
     * @return mixed
     * @throws DataNotFoundException
     * @throws LogicException
     * @throws ModelNotFoundException
     */
    public function answers($params)
    {
        $model = $this->read($params, ['hasSub']);

        if (2 != $model['type']) {
            throw new LogicException('该订单处于非答题状态');
        }

        $topics = $params['topics'];
        // 暂不验证
        return $model->hasSub->save(['order_multi_topic' => $topics]);
    }

    /**
     * 查看结果
     *
     * @param $params
     * @return array[]|mixed
     * @throws DataNotFoundException
     * @throws LogicException
     * @throws ModelNotFoundException
     */
    public function result($params)
    {
        $model = $this->read($params, ['hasSub']);

        // 是否已全部答完
        // 未答题目
        $unanswered = [];
        $typeScore = [];
        foreach ($model['order_multi_topic'] as $key=>$item) {
            $currentT = $key + 1;
            if (0 == strlen($item['answer'])) {
                $unanswered[] = $currentT;
            }

            // 类型对应分数
            $score = $item['options'][$item['answer']]['score'] ?? 0;
            $typeScore[$item['scale_multi_type_id']] = isset($typeScore[$item['scale_multi_type_id']])
                ? $typeScore[$item['scale_multi_type_id']] + $score : $score;
        }

        if (!empty($unanswered)) {
            throw new LogicException('第 ' . implode(',', $unanswered) . ' 题,' . '未作答');
        }

        $orderMultiResult = $model['order_multi_result'];
        if (empty($orderMultiResult) || 3 != $model['type']) {
            // 类型分数排序
            $scores = array_values(array_unique($typeScore));
            ksort($scores);
            $highestScore = $scores[0] ?? -1;
            $nextScore = $scores[1] ?? -1;
            $lowestScore = count($scores) >= 3 ? $scores[count($scores) - 1] : -1;
            $highestType = [];
            $nextType = [];
            $lowestType = [];
            foreach ($typeScore as $type => $score) {
                if ($highestScore == $score) {
                    $highestType[] = $type;
                }
                if ($nextScore == $score) {
                    $nextType[] = $type;
                }
                if ($lowestScore == $score) {
                    $lowestType[] = $type;
                }
            }

            // 生成结果并保存 更新订单状态
            $sortType = [];
            $sortTypeResult = [];
            $mapTypeScore = [];
            foreach ($model['order_multi_type'] as $item) {
                if (in_array($item['id'], $highestType)) {
                    // 最高分
                    $sortType[] = $item['name'];
                    $sortTypeResult[] = [
                        'type_name' => $item['name'],
                        'type_desc' => $item['highest_desc'],
                    ];
                }
                if (in_array($item['id'], $nextType)) {
                    // 次高分
                    $sortType[] = $item['name'];
                    $sortTypeResult[] = [
                        'type_name' => $item['name'],
                        'type_desc' => $item['next_highest_desc'],
                    ];
                }
                if (in_array($item['id'], $lowestType)) {
                    // 最低分
                    $sortType[] = $item['name'];
                    $sortTypeResult[] = [
                        'type_name' => $item['name'],
                        'type_desc' => $item['lowest_desc'],
                    ];
                }
                if (isset($typeScore[$item['id']])) {
                    // 存在类型
                    $mapTypeScore[] = [
                        'type_name' => $item['name'],
                        'type_score' => $typeScore[$item['id']],
                    ];
                }
            }
            $orderMultiResult = [
                'sort_type' => $sortType,
                'sort_type_result' => $sortTypeResult,
                'map_type_score' => $mapTypeScore,
            ];
            Db::connect('communal')->transaction(function () use ($orderMultiResult, $model) {
                $model->save(['type' => 3]);
                $model->hasSub->save(['order_multi_result' => json_encode($orderMultiResult, JSON_UNESCAPED_UNICODE)]);
            });
        }

        $orderMultiResult['scale_multi_id'] = $model['scale_multi_id'];
        return $orderMultiResult;
    }

    /**
     * 解读专家列表
     *
     * @param $params
     * @return mixed
     */
    public function interpreter($params)
    {
        $scaleMultiId = $params['scale_multi_id'];
        $fun = function ($item) {
            unset($item['belongsExpert']);
            return $item;
        };
        return invoke(MultiExpertService::class)
            ->getList($params, ['scale_multi_id'], ['scale_multi_id' => $scaleMultiId], ['belongsExpert'], $fun);
    }

    /**
     * 解读专家详情
     *
     * @param $params
     * @return mixed
     */
    public function interpreterRead($params)
    {
        $scaleMultiId = $params['scale_multi_id'];
        $expertId = $params['expert_id'];
        return invoke(MultiExpertService::class)
            ->getExpertByExpertScale($scaleMultiId, $expertId);
    }

    /**
     * 支付订单
     *
     * @param $order
     * @return string
     * @throws LogicException
     */
    public function payment($order)
    {
        $result = '';
        if (empty($order['number_md5'])) {
            // 订单首次支付
            // 支付订单md5编号
            $numberMd5 = md5($order['number'] . Carbon::now());
            $order->save(['number_md5' => $numberMd5]);
        } else {
            $numberMd5 = $order['number_md5'];
        }
        if ($order['payment_amount'] > 0 && 1 == $order['type']) {
            switch ($order['payment_type']) {
                case 1:
                    // 微信支付
                    $params = [
                        'subject' => $order['scale_multi_name'],
                        'outTradeNo' => $numberMd5,
                        'totalAmount' => (int)bcmul($order['payment_amount'], '100'),
                        'attach' => urlencode(json_encode([
                            // 1 对应 50000_order_multi
                            'order_type' => 1,
                        ], JSON_UNESCAPED_UNICODE)),
                        'notify_url' => config('domain.api') . '/callback/wechatpay'
                    ];
                    $result = WechatpayFacade::v3PayTransactionsApp($params);
                    $result = WechatpayFacade::v3AppSign($result['prepay_id']);
                    break;
                case 2:
                    // 支付宝支付
                    $params = [
                        'subject' => $order['scale_multi_name'],
                        'outTradeNo' => $numberMd5,
                        'totalAmount' => $order['payment_amount'],
                        'optionalArgs' => [
                            'passback_params' => urlencode(json_encode([
                                // 1 对应 50000_order_multi
                                'order_type' => 1,
                            ], JSON_UNESCAPED_UNICODE)),
                            'query_options' => [],
                        ],
                        'notify_url' => config('domain.api') . '/callback/alipay'
                    ];
                    $result = ALiPayFacade::paymentAppPay($params);
                    break;
                default:
                    break;
            }
        }

        return $result;
    }

    /**
     * 支付订单 debug
     *
     * @param $order
     * @return string
     * @throws LogicException
     */
    public function paymentPage($order)
    {
        $result = '';
        if (empty($order['number_md5'])) {
            // 订单首次支付
            // 支付订单md5编号
            $numberMd5 = md5($order['number'] . Carbon::now());
            $order->save(['number_md5' => $numberMd5]);
        } else {
            $numberMd5 = $order['number_md5'];
        }
        if ($order['payment_amount'] > 0 && 1 == $order['type']) {
            switch ($order['payment_type']) {
                case 1:
                    // 微信支付
                    throw new LogicException('暂不支持微信支付');
                    break;
                case 2:
                    // 支付宝支付
                    $params = [
                        'subject' => $order['scale_multi_name'],
                        'outTradeNo' => $numberMd5,
                        'totalAmount' => $order['payment_amount'],
                        // 'totalAmount' => 100,
                        'optionalArgs' => [
                            'passback_params' => urlencode(json_encode([
                                // 1 对应 50000_order_multi
                                'order_type' => 1,
                            ], JSON_UNESCAPED_UNICODE)),
                            'query_options' => [],
                        ],
                        'notify_url' => config('domain.api') . '/callback/alipay'
                    ];
                    $result = ALiPayFacade::paymentPagePay($params);
                    break;
                default:
                    break;
            }
        }

        return $result;
    }

    /**
     * 重新支付
     *
     * @param $params
     * @return array
     * @throws DataNotFoundException
     * @throws LogicException
     * @throws ModelNotFoundException
     */
    public function repay($params)
    {
        $order = $this->getSimpleDetail($params);

        Db::connect('communal')->transaction(function () use ($params, $order) {
            // 获取可用元心币
            $availableCoin = invoke(StatisticsService::class)->getCoinAvailableCountByUserId($params['user_id']);
            $deductCoin = 1 == $params['is_coin'] ? $availableCoin : '0'; // 抵扣用元心币数量
            list($paymentAmount, $deductAmount, $deductCoin) = StringFacade::orderPayment($order['total_amount'], $deductCoin);
            if ($deductCoin != $order['deduct_coin']) {
                // 先把原订单对应元心币解锁再冻结
                invoke(StatisticsService::class)->unfreezeCoin($order['user_id'], $order['deduct_coin']);
                invoke(StatisticsService::class)->freezeCoin($order['user_id'], $deductCoin);
            }
            $order->save(['payment_amount' => $paymentAmount, 'deduct_amount' => $deductAmount, 'deduct_coin' => $deductCoin, 'payment_type' => $params['payment_type']]);
        });

        $payment = $this->payment($order);
        return [
            'order' => $order,
            'payment' => $payment,
        ];
    }

    /**
     * 重新支付 web page debug
     *
     * @param $params
     * @return array
     * @throws DataNotFoundException
     * @throws LogicException
     * @throws ModelNotFoundException
     */
    public function repayPage($params)
    {
        $order = $this->getSimpleDetail($params);
        // 获取可用元心币
        $availableCoin = invoke(StatisticsService::class)->getCoinAvailableCountByUserId($params['user_id']);
        $deductCoin = 1 == $params['is_coin'] ? $availableCoin : '0'; // 抵扣用元心币数量
        list($paymentAmount, $deductAmount, $deductCoin) = StringFacade::orderPayment($order['total_amount'], $deductCoin);
        $order->save(['payment_amount' => $paymentAmount, 'deduct_amount' => $deductAmount, 'deduct_coin' => $deductCoin, 'payment_type' => $params['payment_type']]);

        $payment = $this->paymentPage($order);
        return [
            'order' => $order,
            'payment' => $payment,
        ];
    }

    /**
     * 支付宝回调处理
     *
     * @param $params
     */
    public function aLiPayCallback($params)
    {
        Log::debug('Callback_ALiPay_order_multi_params: ' . var_export($params, true));
        // 签名验证后
        $check = ALiPayFacade::paymentCommonVerifyNotify($params);
        Log::debug('Callback_ALiPay_order_multi_out_trade_no: ' . $params['out_trade_no'] . ',验签结果: ' . $check);
        if (!$check) {
            return ;
        }

        $update = [];
        $tradeStatus = $params['trade_status'] ?? '';
        if (isset($params['trade_status']) && 'TRADE_SUCCESS' == $params['trade_status']) {
            // 支付成功
            // Log::debug('Callback_ALiPay_order_multi_debug');
            // 销量 + 1
            // 冻结元心币消耗
            // 增加元心币消费明细
            // 去事件里面搞吧...
            event('OrderMultiPaySuccess', ['out_trade_no' => $params['out_trade_no'], 'trade_status' => $tradeStatus]); // 可队列
        } else {
            // 更新订单信息
            $update['trade_status'] = $tradeStatus;
            OrderMultiModel::where('number_md5', $params['out_trade_no'])->update($update);
        }
    }

    /**
     * 微信回调处理
     *
     * @param $params
     */
    public function wechatPayCallback($params)
    {
        Log::debug('Callback_wechatPay_order_multi_params: ' . var_export($params, true));
        $update = [];
        $tradeStatus = $params['trade_state'] ?? '';
        if (isset($params['trade_state']) && 'SUCCESS' == $params['trade_state']) {
            // 支付成功
            // Log::debug('Callback_ALiPay_order_multi_debug');
            // 销量 + 1
            // 冻结元心币消耗
            // 增加元心币消费明细
            // 去事件里面搞吧...
            event('OrderMultiPaySuccess', ['out_trade_no' => $params['out_trade_no'], 'trade_status' => $tradeStatus]); // 可队列
        } else {
            // 更新订单信息
            $update['trade_status'] = $tradeStatus;
            OrderMultiModel::where('number_md5', $params['out_trade_no'])->update($update);
        }
    }

    /**
     * 支付成功
     *
     * @param $outTradeNo
     * @param string $tradeStatus
     */
    public function paySuccess($outTradeNo, $tradeStatus = '')
    {
        $model = OrderMultiModel::where('number_md5', $outTradeNo)->findOrEmpty();
        if (!$model->isEmpty() && 1 == $model['type']) {
            // 销量 + 1
            // 去事件里面搞吧...
            Db::connect('communal')->transaction(function () use ($model, $outTradeNo, $tradeStatus) {
                $model->save(['type' => 2, 'trade_status' => $tradeStatus]);
                // 冻结元心币消耗
                // 增加元心币消费明细
                invoke(StatisticsService::class)->consumeFreezeCoin($model['user_id'], $model['deduct_coin'], 2);
                // 销量 + 1
                invoke(MultiService::class)->salesNumberPlus($model['scale_multi_id']);
                // 增加累积消费 并 试图返点
                invoke(StatisticsService::class)->accrueCost($model['user_id'], $model['payment_amount']);
            });
        }
    }
}
